/**
 * 
 */
package edu.arizona.bio5.onto.decomposition;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;

import org.semanticweb.owlapi.apibinding.OWLManager;
import org.semanticweb.owlapi.model.IRI;
import org.semanticweb.owlapi.model.OWLAxiom;
import org.semanticweb.owlapi.model.OWLClass;
import org.semanticweb.owlapi.model.OWLClassAxiom;
import org.semanticweb.owlapi.model.OWLOntology;
import org.semanticweb.owlapi.model.OWLOntologyManager;

import uk.ac.manchester.cs.owlapi.modularity.ModuleType;
import edu.arizona.bio5.onto.util.OntologyUtils;
import edu.arizona.bio5.timers.StaticTimers;

/**
 * Class to evaluate performance gains from using the incremental AD algorithm
 * 
 * @author PavelK
 *
 */
public class IncrementalADEvaluator {

	private static final String ONTO_URI = "file:eval/data/CHEBI/CHEBI_1k.owl";
	private static final int NUMBER_OF_AXIOMS_TO_ADD = 5;
	private static final ModuleType MODULE_TYPE = ModuleType.BOT;
	private static final OWLOntologyManager mMgr = OWLManager.createOWLOntologyManager();
	
	static double mAffRatio = 0;
	
	private static OWLOntology loadOntology(String uri, Set<OWLAxiom> toAddAxioms) throws Exception {		
		OWLOntology fullOntology = mMgr.loadOntologyFromOntologyDocument( IRI.create(uri) );
		
		OntologyUtils.prepareForSyntacticModularity(fullOntology);
		
		List<OWLClass> classes = new ArrayList<OWLClass>(fullOntology.getClassesInSignature());
		
		Collections.shuffle(classes, new Random(System.currentTimeMillis()));
		
		for (OWLClass clazz : classes) {
			Set<OWLClassAxiom> classAxioms = fullOntology.getAxioms(clazz);
			int logAxiomCount = 0;
			
			if (classAxioms.size() > 1) {				
				
				for (OWLClassAxiom classAxiom : classAxioms) {
					if (classAxiom.isLogicalAxiom() && ++logAxiomCount > 1) {
						toAddAxioms.add(classAxiom);
					}
				}
			}
			
			if (toAddAxioms.size() > NUMBER_OF_AXIOMS_TO_ADD) {
				break;
			}
		}
		
		//toAddAxioms.addAll(SetUtils.pickRandomSubset(fullOntology.getLogicalAxioms(), NUMBER_OF_AXIOMS_TO_ADD));
		//delete these axioms from the ontology
		mMgr.removeAxioms(fullOntology, toAddAxioms);
		
		System.out.println("Axioms to add: " + toAddAxioms.size() + ", initial ontology size: " + fullOntology.getLogicalAxiomCount());
				
		return fullOntology;
	}	
	
	/*
	 * Take some reasonable ontology, say, GO or ChEBI
	 * Take some random collection of axioms, say, 50%
	 * Decompose the other 50% and add each axiom to the decomposition.
	 * Measure: 
	 * - % of ontology re-decomposed
	 * - time it takes to incorporate an axiom (graph, should be fairly constant)
	 * Compare with the standard algorithm
	 */
	public static void main(String... args) throws Exception {
		Set<OWLAxiom> toAddAxioms = new HashSet<OWLAxiom>();
		OWLOntology ontology = loadOntology(ONTO_URI, toAddAxioms);
		//do the initial decomposition
		AtomicDecomposition incAD = decompose(ontology);
		
		System.out.println("Initial decomposition done");
		
		//now start the evaluation loop
		for (OWLAxiom axiom : toAddAxioms) {
			evalAddition(axiom, incAD, ontology);
			
			System.out.println("Axiom added: " + axiom);
		}
		
		//mAffRatio /= toAddAxioms.size();
		mAffRatio = mAffRatio * 100 / toAddAxioms.size();
		
		System.out.println("The average percentage of affected atoms: " + mAffRatio);
		
		StaticTimers.printAll();
	}

	private static AtomicDecomposition evalAddition(OWLAxiom axiom, AtomicDecomposition incAD, OWLOntology ontology) {
		IncrementalADAlgorithm algo = new IncrementalADAlgorithm(ontology);
		int atomNumber = incAD.getAtoms().size();
		// eval incremental
		StaticTimers.start("incremental");
		
		incAD = algo.addAxiom(incAD, axiom);
		
		StaticTimers.stop("incremental");
		
		mAffRatio += 1d * algo.getAffectedAtomNumber() / atomNumber;
		System.out.println(mAffRatio);
		//eval non-incremental
		StaticTimers.start("non-incremental");
		
		AtomicDecomposition ad = new ChiaraDecompositionAlgorithm(MODULE_TYPE).decompose(ontology.getOWLOntologyManager(), ontology);
		
		ad.buildBasicTermIndex();
		
		StaticTimers.stop("non-incremental");
		
		return incAD;
	}

	private static AtomicDecomposition decompose(OWLOntology ontology) {
		return new ChiaraDecompositionAlgorithm(MODULE_TYPE).decompose(mMgr, ontology);
	}
}
